package com.jsjvm.server.dbgproxy.jdwp.cmdsets;

import java.io.IOException;

import com.jsjvm.server.RemoteClassLoadingImpl;
import com.jsjvm.server.dbgproxy.JDPABackEnd;
import com.jsjvm.server.dbgproxy.JDWPCommandSet;
import com.jsjvm.server.dbgproxy.jdwp.data.JDWPInputStream;
import com.jsjvm.server.dbgproxy.jdwp.data.JDWPOutputStream;
import com.jsjvm.server.dbgproxy.jdwp.data.JDWPRequestHeader;
import com.jsjvm.shared.vmcmds.VMCommand;

@SuppressWarnings("serial")
public class JDWPVirtualMachineCommandSet extends JDWPCommandSet {
	private static final int VERSION_CMD = 1;
	private static final int CLASSES_BY_SIGNATURE_CMD = 2;
	private static final int ALL_CLASSES_CMD = 3;
	private static final int ALL_THREADS_CMD = 4;
	private static final int TOP_LEVEL_THREAD_GROUPS_CMD = 5;
	public  static final int DISPOSE_CMD = 6;
	private static final int ID_SIZES_CMD = 7;
	private static final int SUSPEND_CMD = 8;
	private static final int RESUME_CMD = 9;
	private static final int EXIT_CMD = 10;
	private static final int CREATE_STRING_CMD = 11;
	private static final int CAPABILITIES_CMD = 12;
	private static final int CLASS_PATHS_CMD = 13;
	private static final int DISPOSE_OBJECTS_CMD = 14;
	private static final int HOLD_EVENTS_CMD = 15;
	private static final int RELEASE_EVENTS_CMD = 16;
	private static final int CAPABILITIES_NEW_CMD = 17;
	private static final int REDEFINE_CLASSES_CMD = 18;
	private static final int SET_DEFAULT_STRATUM_CMD = 19;
	private static final int ALL_CLASSES_GENERIC_CMD = 20;
	private static final int INSTANCE_COUNTS_CMD = 21;

	public static final int VIRTUAL_MACHINE_SET = 1;
	
	public JDWPVirtualMachineCommandSet() {
		this.put(VERSION_CMD, new VersionHandler());
		this.put(ID_SIZES_CMD, new IDSizes());
		this.put(ALL_THREADS_CMD, new AllThreads());
		this.put(CAPABILITIES_CMD, new Capabilities(7));
		this.put(CAPABILITIES_NEW_CMD, new Capabilities(32));
		this.put(CLASSES_BY_SIGNATURE_CMD, new ClassesBySignature());
		this.put(CLASS_PATHS_CMD, new ClassPaths());
		this.put(TOP_LEVEL_THREAD_GROUPS_CMD, new ThreadsGroups());
		
		this.put(REDEFINE_CLASSES_CMD, getNIHandler());
		this.put(SET_DEFAULT_STRATUM_CMD, getNIHandler());
		this.put(INSTANCE_COUNTS_CMD, getNIHandler());
		this.put(ALL_CLASSES_GENERIC_CMD, getNIHandler());
	}

	@Override
	public int getCommandSetNumber() {
		return VIRTUAL_MACHINE_SET;
	}

	
	/**
	 * Send version information about VM
	 */
	private static class VersionHandler extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			os.appendString("JSJVM - a browser hoster JVM");
			os.appendInt(1);
			os.appendInt(50);
			os.appendString("1.5");
			os.appendString("JSJVM");
			os.commitReplyPacket(hdr);
			return null;
		}
	}
	
	/**
	 * Sizes of ID fields in this implementation (all of them are ints)
	 */
	private static class IDSizes extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			for (int i = 0; i < 5; i++)
				os.appendInt(4);
			os.commitReplyPacket(hdr);
			return null;
		}
	}
	
	/**
	 * Running threads - only one
	 */
	public static class AllThreads extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			os.appendInt(1);
			os.appendInt(JDPABackEnd.THREAD_ID);
			os.commitReplyPacket(hdr);
			return null;
		}
	}

	/**
	 * Debug capabilities for this VM: none for now.
	 */
	public static class Capabilities extends JDWPCommandHandler {
		private int capabilitiesNo = 0;
		public Capabilities(int capabilitiesNo) {
			this.capabilitiesNo = capabilitiesNo;
		}
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			for (int i = 0; i < capabilitiesNo; i++)
				os.appendByte((byte) 0);
			os.commitReplyPacket(hdr);
			return null;
		}
	}
	
	/**
	 * Give the class ID for some signature
	 */
	public static class ClassesBySignature extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			String sig = is.readString();
			System.out.println("Requested class with sig: " + sig);
			
			// FIXME: really give some identifiers when loaded
			os.appendInt(0); 	// 0 classes matching
			os.commitReplyPacket(hdr);
			return null;
		}
	}
	
	/**
	 * Top Level Thread groups - only one
	 */
	public static class ThreadsGroups extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			os.appendInt(1);
			os.appendInt(JDPABackEnd.THREAD_GROUP_ID);
			os.commitReplyPacket(hdr);
			return null;
		}
	}
	
	/**
	 * Class paths used by the server-side loader
	 */
	public static class ClassPaths extends JDWPCommandHandler {
		@Override
		public VMCommand handleCommand(JDWPRequestHeader hdr, JDWPInputStream is,
				JDWPOutputStream os) throws IOException {
			os.appendString("/");
			os.appendInt(RemoteClassLoadingImpl.CLASS_PATH.length);	// hardcoded classpaths
			for (String cp : RemoteClassLoadingImpl.CLASS_PATH)
				os.appendString(cp);
			os.appendInt(0);	// no bootclasspaths
			os.commitReplyPacket(hdr);
			return null;
		}
	}
}
